package com.palmcity.rtti.excode;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import org.apache.log4j.Logger;

import com.palmcity.rtti.base.DTICity;
import com.palmcity.rtti.base.DTIEvent;
import com.palmcity.rtti.base.DTIJam;
import com.palmcity.rtti.base.DTILink;
import com.palmcity.rtti.base.DTIMesh;
import com.palmcity.rtti.base.EventLocation;
import com.palmcity.rtti.base.EventTime;
import com.palmcity.rtti.base.PassingRoad;
import com.palmcity.rtti.util.TrafficUtils;

/**
 * <p>
 * DtiNewDecoder
 * </p>
 * <p>
 * DTI1.5单个城市数据解析新类，用byteBuffer类解析
 * </p>
 * @author 周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>zhouhuabin</td>
 *          <td>2015年1月5日 上午10:05:42</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class DtiByteBufferDecoder {
	public static final String PUBLISH_DTI_CHARSET_GBK = "GBK";

	private static Logger log = Logger.getLogger(DtiByteBufferDecoder.class);

	/** dti发布数据b2c格式的起始位置 */
	public static final int PUBLISH_DTI_STARTPOS_B2C = 68;
	/** dti发布数据b2b格式的起始位置 */
	public static final int PUBLISH_DTI_STARTPOS_B2B = 0;
	/** 实时路况 */
	public static final byte DTIJAMSIGN = 1;
	/** 预测路况 */
	public static final byte DTIPREDICTJAMSIGN = 3;

	/** 变量:DTI1.5每timeid间隔的分钟数,类型:double */
	public static final double DTI15_TIMEID_INTERVAL = 0.5;

	/**
	 * 解析DTI发布时间
	 * @param timeData
	 * @param dtiCity
	 */
	@SuppressWarnings("unused")
	private int parseDtiPublishTime(byte[] timeData) {
		try {
			/** * 信息分割符号 */
			int infoSeg = timeData[0];
			/** * 数据头参数 */
			int contentHeadSeg = timeData[1];
			/** * 节目编号 */
			int programID = timeData[2];
			/** * 项更新标识 */
			int conUpdateFlag = (timeData[3] >> 6);
			/** * 内容项编号 */
			int contentID = (timeData[3] & 0x003f);
			String contentMessage = null;
			if (contentID == 0) {
				contentMessage = "节目头";
			} else if (contentID == 1) {
				contentMessage = "道路交通状况和旅行时间";
			} else if (contentID == 2) {
				contentMessage = "交通管制和事件信息";
			} else if (contentID == 3) {
				contentMessage = "停车诱导信息";
			} else {
				contentMessage = "未定义";
			}

			/** * 内容项分类 */
			int conCategory = timeData[4] >> 4;
			String conCategoryMessage = null;
			if (conCategory == 0) {
				conCategoryMessage = "无指定";
			} else if (conCategory == 1) {
				conCategoryMessage = "道路交通状况和旅行时间";
			} else if (conCategory == 2) {
				conCategoryMessage = "交通管制和事件信息";
			} else if (conCategory == 3) {
				conCategoryMessage = "停车诱导信息";
			} else {
				conCategoryMessage = "未定义";
			}
			log.debug("解析到DTI内容项contentMessage=" + contentMessage + ",conCategoryMessage=" + conCategoryMessage);
			/** * 信息发布时间小时的高三位 */
			int highHH = timeData[4] & 0x07;
			/** * 信息发布时间小时的低两位 */
			int lowHH = (timeData[5] & 0xc0) >>> 6;
			/** * 信息发布时间分钟 */
			int MM = timeData[5] & 0x3f;
			// 小时数
			int HH = (highHH << 2) | (lowHH & 0x03);
			int timeId = (int) (HH * 60 / DTI15_TIMEID_INTERVAL + MM / DTI15_TIMEID_INTERVAL);
			return timeId;
		} catch (Exception e) {
			log.error("解析DTI发布时间异常,msg=" + e.getLocalizedMessage(), e);
			return 0;
		}
	}

	/**
	 * 解析数据文件中的路况和平均行驶速度
	 * @param jamSegmentData
	 * @param i
	 * @param linkNum
	 * @param dtiMesh
	 * @param timekind
	 * @param startLinkID
	 * @param roadClass
	 * @param output
	 * @return
	 */
	@SuppressWarnings("unused")
	private int parseDtiJamWithSpeedData(byte[] jamSegmentData, int i, int linkNum, DTIMesh dtiMesh, int infoType, int startLinkID, int roadClass) {
		int cur = i;
		for (int index = 0; index < linkNum; index++) {
			try {
				DTIJam dtiJam = new DTIJam();
				DTILink link = new DTILink();
				link.setLinkId((short) (startLinkID + index));
				// link.setRoadClass((short) (roadClass));
				link.setRoadClass((short) (TrafficUtils.getRealRoadClass(roadClass)));
				link.setMeshId(dtiMesh.getMeshId());
				dtiJam.setLink(link);
				dtiMesh.addDTI(dtiJam);
				dtiJam.setInfoType(infoType==0?DTIJAMSIGN:DTIPREDICTJAMSIGN);
				byte info = jamSegmentData[cur++];
				if (((info & 0xff) >>> 6) != 0 || (info & 0x3f) != 0) {
					dtiJam.setCondition(TrafficUtils.getRoadCondition((info & 0xff) >>> 6));
					int extendFlag = ((info & 0x38) >>> 3);
					if (extendFlag == 1) {
						dtiJam.setSpeed(getAvgSpeed(info));
					} else if (extendFlag == 2) {
						dtiJam.setSpeed(getAvgSpeed(info));
						/** TODO  扩展信息1 目前扩展信息没有用到 */
						int firExtentInfo1 = jamSegmentData[cur++];
						int secExtentInfo1 = jamSegmentData[cur++];
					} else if (extendFlag == 3) {
						/**	 * TODO  扩展信息1 目前扩展信息没有用到 */
						int firExtentInfo1 = jamSegmentData[cur++];
						int secExtentInfo1 = jamSegmentData[cur++];
						/** 扩展信息2 */
						int extentInfo2 = jamSegmentData[cur++];
						dtiJam.setSpeed(getAvgSpeed(info, extentInfo2));
					} else if (extendFlag == 4) {
						/** 扩展信息2 */
						int extentInfo2 = jamSegmentData[cur++];
						dtiJam.setSpeed(getAvgSpeed(info, extentInfo2));
					} 
					
				}
			} catch (Exception e) {
				log.error("解析DTIJam对象异常,msg=" + e.getLocalizedMessage(), e);
			}
		}
		return cur;
	}

	/**
	 * 解析平均旅行时间，数据结构中有扩展信息2时用
	 * @param baseByte 速度基本字节
	 * @param extentByte 速度扩展字节
	 * @return
	 */
	private float getAvgSpeed(byte baseByte, int extentByte) {
		return (((extentByte & 0xff) << 3) | (baseByte & 0x07)) * 0.1f;
	}

	/**
	 * 解析平均旅行时间 ,数据结构中无扩展信息2时用
	 * @param baseByte
	 * @return
	 */
	private float getAvgSpeed(byte baseByte) {
		return (baseByte & 0x07) * 0.1f;
	}

	/**
	 * 解析路况和事件
	 * @param dtiSegmentByte 要解析的字节数组
	 * @param dtiMesh 要存放的mesh对象
	 */
	@SuppressWarnings("unused")
	private void parseDtiSegment(byte[] dtiSegmentByte, DTIMesh dtiMesh) {
		try {
			int i = 0;
			/** * 信息分割符 */
			int infoSeg = dtiSegmentByte[0];
			/** * 数据段参数 */
			int DataSeg = dtiSegmentByte[1];
			/** * 数据关联标识 */
			int DSL = (dtiSegmentByte[2] & 0x80) >>> 7;
			String isRelated = null;
			if (DSL == 0) {
				isRelated = "不与后续数据段关联";
			} else if (DSL == 1) {
				isRelated = "与后续数据段关联";
			} else {
				isRelated = "数据段关联标识出错 ";
			}
			/**	 * 数据段容量高 7 位 */
			int highDataLength = dtiSegmentByte[2] & 0x7f;
			/** * 数据段容量低 8 位 */
			int lowDataLength = dtiSegmentByte[3];
			int dataLength = (highDataLength << 8 & 0x7f00) | (lowDataLength & 0xff);
			i += 4;
			while (i < (dataLength + 4)) {
				/** * 解析动态交通信息， */
				if (DataSeg == 0x40) {
					try {
						byte ctb0 = dtiSegmentByte[i++];
						/** * 信息类型 */
						int infoType = (ctb0 & 0x80) >>> 7;
						/** * 旅行时间类别 */
						int timeKind = (ctb0 & 0x40) >>> 6;
						/** * 信息数标识 */
						int infoNum = (ctb0 & 0x20) >>> 5;
						/**	 * 连续路链数高 4 位	 */
						int highLinkNum = ctb0 & 0x0f;
						/** * 连续路链数 低 8 位 */
						int lowLinkNum = dtiSegmentByte[i++];
						int linkNum = (highLinkNum << 8 & 0x0f00) | (lowLinkNum & 0xff);
						int ctb2 = dtiSegmentByte[i++];
						int roadClass = (ctb2 & 0x70) >>> 4;
						int highStartLinkID = ctb2 & 0x0f;
						int lowStartLinkID = dtiSegmentByte[i++];
						int startLinkID = (highStartLinkID << 8 & 0x0f00) | (lowStartLinkID & 0xff);
						if (infoType == 0) {// 带速度的路况
							byte[] jamData = Arrays.copyOfRange(dtiSegmentByte, i,i+dataLength);
							i = parseDtiJamWithSpeedData(jamData, i, linkNum, dtiMesh, timeKind, startLinkID, roadClass);
						} else if (infoType == 1) {
							log.error("DTI1.0解析路链暂时不支持,meshid=" + dtiMesh.getMeshId());
						}
					} catch (Exception e) {
						log.error("解析路链对象异常meshid=" + dtiMesh.getMeshId() + ",msg=" + e.getLocalizedMessage(), e);
					}
				}
				/** * 解析事件和交通管制信息 */
				else if (DataSeg == 0x41) {
					int re1b0 = dtiSegmentByte[i++];
					/** 信息扩展标识 */
					int extentSign = (re1b0 & 0xc0) >>> 6;
					int linkNum = re1b0 & 0x3f;
					EventLocation eventLocation = new EventLocation();
					DTIEvent dtiEvent = new DTIEvent();
					int pos = 1;
					eventLocation.setAffectLinks(linkNum);
					int re1b1 = dtiSegmentByte[i++];
					int eventType = (((re1b1 & 0xf0) >>> 4) + 256) % 256;
					int controlType = ((re1b1 & 0x0f) + 256) % 256;
					int re1b2 = dtiSegmentByte[i++];
					int unit = (re1b2 & 0xc0) >>> 6;
					int controlLength = re1b2 & 0x3f;
					eventLocation.setTotalDistance(controlLength);
					eventLocation.setUnit(unit);
					dtiEvent.setEventCtlType(controlType);
					dtiEvent.setEventType(eventType);

					while (pos <= linkNum) {
						/** * 起始路链信息 */
						if (pos == 1) {
							int re1b3 = dtiSegmentByte[i++];
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte[i++];
							int LinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte[i++] & 0x07;
							if (nameSign == 1) {
								int nameLength = dtiSegmentByte[i++] & 0xff;
								byte[] nameBytes = Arrays.copyOfRange(dtiSegmentByte,i,i+nameLength);
								i = i + nameLength;
								try {
									String startName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
									eventLocation.setStartName(startName);
								} catch (UnsupportedEncodingException e) {
									log.error("解析路链名称异常,msg=" + e.getLocalizedMessage(), e);
								}
							}
							DTILink startLink = new DTILink();
							startLink.setLinkId((short) LinkID);
							startLink.setRoadClass((short) roadClass);
							startLink.setMeshId(dtiMesh.getMeshId());
							eventLocation.setStartLink(startLink);
							pos++;
						}

						/*** 终止路链信息 */
						else if (pos == 2) {
							int re1b3 = dtiSegmentByte[i++];
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte[i++];
							int endLinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte[i++] & 0x07;

							DTILink endLink = new DTILink();
							endLink.setLinkId((short) endLinkID);
							endLink.setRoadClass((short) roadClass);
							if (meshSign == 1) {
								int meshid = TrafficUtils.getGridID(dtiSegmentByte, i);
								endLink.setMeshId(meshid);
								i += 3;
							} else {
								endLink.setMeshId(dtiMesh.getMeshId());
							}
							eventLocation.setEndLink(endLink);
							if (nameSign == 1) {
								int nameLength = dtiSegmentByte[i++] & 0xff;
								byte[] nameBytes = Arrays.copyOfRange(dtiSegmentByte,i,i+nameLength);
								i = i +nameLength;
								try {
									String endName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
									eventLocation.setEndName(endName);
								} catch (UnsupportedEncodingException e) {
									log.error("解析终止路链名称异常,msg=" + e.getLocalizedMessage(), e);
								}
							}
							pos++;
						}/** * 途径路链信息  */
						else{
							int re1b3 = dtiSegmentByte[i++];
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte[i++];
							int passLinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte[i++] & 0x07;
							int continusLinkNum = dtiSegmentByte[i++];
							int meshid = dtiMesh.getMeshId();
							/**
							 * 如果途径路链和起始路链不在一个图幅内则解析途径路链的图幅号信息，否则用起始路链的图幅号
							 */
							if (meshSign == 1) {
								meshid = TrafficUtils.getGridID(dtiSegmentByte, i);
								i += 3;
							}
							for (int passings = 0; passings < continusLinkNum; passings++) {
								DTILink passLink = new DTILink();
								passLink.setMeshId(meshid);
								passLink.setLinkId((short) (passLinkID + passings));
								passLink.setRoadClass((short) roadClass);
								PassingRoad passingRoad = new PassingRoad();
								passingRoad.setLink(passLink);
								if (nameSign == 1) {
									int nameLength = dtiSegmentByte[i++] & 0xff;
									byte[] nameBytes = Arrays.copyOfRange(dtiSegmentByte,i,i+nameLength);
									i = i +nameLength;
									try {
										String passName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
										passingRoad.setName(passName);
									} catch (UnsupportedEncodingException e) {
										log.error("解析途经路链名称异常,msg=" + e.getLocalizedMessage(), e);
									}

								}
								pos++;
								eventLocation.addPassRoad(passingRoad);
							}
						}
					}

					/**
					 * 对扩展信息 1 进行解码
					 */
					if (extentSign >= 1) {
						int eventCtrlsType = (dtiSegmentByte[i++] + 256) % 256;
						int DTIEventsType = (dtiSegmentByte[i++] + 256) % 256;
						byte REEB2 = dtiSegmentByte[i++];
						byte REEB3 = dtiSegmentByte[i++];
						int disUnit = (REEB2 >>> 6 & 0x02) | (REEB3 >>> 7 & 0x01);
						int startDistance = REEB2 & 0x7f;
						int endDistance = REEB3 & 0x7f;
						eventLocation.setStartDistance(startDistance);
						eventLocation.setEndDistance(endDistance);
						eventLocation.setUnit(disUnit);
						dtiEvent.setEventCtlSType(eventCtrlsType);
						dtiEvent.setEventSType(DTIEventsType);
					}
					dtiEvent.setEventLocation(eventLocation);
					/** * 对扩展信息 2 进行解码 */
					if (extentSign >= 2) {
						int timeType = dtiSegmentByte[i++] >>> 7 & 0x01;
						int REB5 = dtiSegmentByte[i++];
						int REB6 = dtiSegmentByte[i++];
						int startYYYY = (REB5 << 4 | (REB6 >>> 4 & 0x0F)) & 0x0FFF;
						int endYYYY = startYYYY + (REB6 & 0x0F);
						int REEB7 = dtiSegmentByte[i++];
						int startMM = REEB7 >>> 4 & 0x0f;
						int endMM = REEB7 & 0x0f;
						int REEB8 = dtiSegmentByte[i++];
						int startDD = REEB8 >>> 3 & 0x1f;
						int startHHHigh3 = REEB8 & 0x07;
						int REEB9 = dtiSegmentByte[i++];
						int startHHLow2 = REEB9 >>> 6 & 0x03;
						int startHH = (startHHHigh3 << 2 & 0x1f) | (startHHLow2 & 0x03);
						int startTT = REEB9 & 0x3f;
						int REEB10 = dtiSegmentByte[i++];
						int endDD = REEB10 >>> 3 & 0x1f;
						int endHHHigh3 = REEB10 & 0x07;
						int REEB11 = dtiSegmentByte[i++];
						int endHHLow2 = REEB11 >>> 6 & 0x03;
						int endHH = (endHHHigh3 << 2 & 0x1f) | (endHHLow2 & 0x03);
						int endTT = REEB11 & 0x3f;
						EventTime eventTime = new EventTime();
						eventTime.setEndYYYY(endYYYY);
						eventTime.setEndDD(endDD);
						eventTime.setEndHH(endHH);
						eventTime.setEndMM(endMM);
						eventTime.setEndTT(endTT);
						eventTime.setStartYYYY(startYYYY);
						eventTime.setStartDD(startDD);
						eventTime.setStartHH(startHH);
						eventTime.setStartMM(startMM);
						eventTime.setStartTT(startTT);
						eventTime.setTimeType(timeType);
						dtiEvent.setEventTime(eventTime);
					}

					/**
					 * 对扩展信息 3 进行解码
					 */
					if (extentSign == 3) {
						int lon = 0;
						for (int j = 0; j < 4; j++) {
							lon = lon | ((dtiSegmentByte[i++] & 0xFF) << 8 * j);
						}
						eventLocation.setLon(lon);
						int lat = 0;
						for (int j = 0; j < 4; j++) {
							lat = lat | ((dtiSegmentByte[i++] & 0xFF) << 8 * j);
						}
						eventLocation.setLat(lat);
						int remarkELength = dtiSegmentByte[i++] & 0xFF;
						if (remarkELength > 0) {
							byte[] remarkEBytes = Arrays.copyOfRange(dtiSegmentByte,i,i+remarkELength);
							i = i +remarkELength;
							try {
								String remarkE = new String(remarkEBytes, PUBLISH_DTI_CHARSET_GBK);
								dtiEvent.setRemarkEvent(remarkE);
							} catch (UnsupportedEncodingException e) {
								log.error("解析事件描述异常,msg=" + e.getLocalizedMessage(), e);
							}
						}

						int remarkCLength = dtiSegmentByte[i++] & 0xFF;
						if (remarkCLength > 0) {
							byte[] remarkCBytes = Arrays.copyOfRange(dtiSegmentByte,i,i+remarkCLength);
							i = i +remarkCLength;
							try {
								String remarkC = new String(remarkCBytes, PUBLISH_DTI_CHARSET_GBK);
								dtiEvent.setRemarkControl(remarkC);
							} catch (UnsupportedEncodingException e) {
								log.error("解析管制描述异常,msg=" + e.getLocalizedMessage(), e);
							}
						}
					}
					dtiMesh.addDTI(dtiEvent);
				}
				/**
				 * 解析停车诱导信息
				 */
				else if (DataSeg == 0x42) {
					log.error("暂时不支持停车诱导对象解析.");
				}
			}
		} catch (Exception e) {
			log.error("解析路链对象异常,msg=" + e.getLocalizedMessage(), e);
		}
	}

	/**
	 * dti格式解析入口
	 * @param bitData 原始数据
	 * @param cityCode 城市代码
	 * @return 解析后的对象
	 */
	public DTICity decodeDti(byte[] bitData, String cityCode) {
		if (bitData == null || bitData.length == 0) {
			return null;
		}
		DTIMesh dtiMesh = null;
		DTICity sinCityData = new DTICity();
		sinCityData.setCityCode(cityCode);
		for (int i = 0; i < bitData.length;) {
			// 解析网格字节
			if (bitData[i] == 0x1E && bitData[i + 1] == 0x31) {
				dtiMesh = new DTIMesh();
				sinCityData.addMesh(dtiMesh);
				int meshidLength = 11;
				byte[] listByteProgram = Arrays.copyOfRange(bitData, i, i+meshidLength);
				i = i + meshidLength;
				dtiMesh.setMeshId(TrafficUtils.getGridID(listByteProgram, 8));
			} // 解析发布时间
			else if (bitData[i] == 0x1E && bitData[i + 1] == 0x33) {
				int timeLength = 6;
				byte[] publishTimeData = Arrays.copyOfRange(bitData, i, i+timeLength);
				i = i + timeLength;
				sinCityData.setTimeId(parseDtiPublishTime(publishTimeData));
			} // 解析道路路况和事件
			else if (bitData[i] == 0x1F && (bitData[i + 1] == 0x40 | bitData[i + 1] == 0x41 | bitData[i + 1] == 0x42 | bitData[i + 1] == 0x43)) {
				int highDataLength = bitData[i + 2] & 0x7f;
				/** * 数据段容量低 8 位 */
				int lowDataLength = bitData[i + 3];
				int dataLength = (highDataLength << 8 & 0x7f00) | (lowDataLength & 0xff);
				byte[] segmentData = Arrays.copyOfRange(bitData, i, i+dataLength+4);
				i = i + dataLength + 4;
				parseDtiSegment(segmentData, dtiMesh);
			}
		}
		return sinCityData;
	}

}
